package com.sun.dream.fish.secret.asymmetry;

import java.math.BigInteger;
import java.util.Random;

public class BlumGoldwasserEncryption {
    private final BigInteger p;
    private final BigInteger q;
    private final BigInteger n;
    private final BigInteger x0;
    private static final BigInteger TWO = BigInteger.valueOf(2);

    public BlumGoldwasserEncryption(BigInteger p, BigInteger q, BigInteger x0) {
        this.p = p;
        this.q = q;
        this.n = p.multiply(q);
        this.x0 = x0;
    }

    public String encrypt(String message) {
        StringBuilder encryptedMessage = new StringBuilder();

        for (char c : message.toCharArray()) {
            BigInteger m = BigInteger.valueOf(c);

            BigInteger b = BigInteger.valueOf(new Random().nextInt(2));

            BigInteger y = x0.modPow(TWO, n).multiply(b.modPow(TWO, n)).mod(n);

            String yHex = String.format("%04X", y);
            encryptedMessage.append(m.longValue());
            encryptedMessage.append(yHex);
        }

        return encryptedMessage.toString();
    }

    public String decrypt(String encryptedMessage) {
        StringBuilder decryptedMessage = new StringBuilder();

        for (int i = 0; i < encryptedMessage.length(); i += 4) {
            String yHex = encryptedMessage.substring(i, i + 4);

            BigInteger y = new BigInteger(yHex, 16);

            BigInteger pPlus1Div4 = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4));
            BigInteger qPlus1Div4 = q.add(BigInteger.ONE).divide(BigInteger.valueOf(4));

            BigInteger pResult = y.modPow(pPlus1Div4, p);
            BigInteger qResult = y.modPow(qPlus1Div4, q);

            BigInteger x1 = pResult.multiply(q).multiply(q.modInverse(p));
            BigInteger x2 = qResult.multiply(p).multiply(p.modInverse(q));

            BigInteger x = x1.add(x2).mod(n);

            BigInteger m = x.mod(TWO);

            char decryptedChar = (char) m.longValue();
            decryptedMessage.append(decryptedChar);
        }

        return decryptedMessage.toString();
    }

    public static void main(String[] args) {
        BigInteger p = new BigInteger("383");
        BigInteger q = new BigInteger("503");
        BigInteger x0 = new BigInteger("123");

        BlumGoldwasserEncryption encryption = new BlumGoldwasserEncryption(p, q, x0);
        String message = "Hello World!";
        String encrypted = encryption.encrypt(message);

        System.out.println("Encrypted message: " + encrypted);

        String decrypted = encryption.decrypt(encrypted);
        System.out.println("Decrypted message: " + decrypted);
    }
}